#include "mutex.h"
#include "thread.h"
#include "conductor.h"

/**
 * initialize the specified mutex
 * @param pmutex: the descriptor of mutex
 * @return: void
 */
void mx_mutex_init (mx_mutex_pt pmutex)
{
    pmutex->suspend = mx_null;
    mx_atom_flag_init(&pmutex->flag);
}

/**
 * enter the mutex
 * @param pmutex: the descriptor of mutex
 * @return: void
 */
void mx_mutex_enter (mx_mutex_pt pmutex)
{
    mx_bool_t lock;
    mx_thread_pt activetd = mx_conductor_active();

    if (activetd != mx_null)
    {
        /* try to get lock, the lock id use thread descritor */
        while (!(lock = mx_atom_flag_lock(&pmutex->flag, activetd)))
        {
            /* enter the suspend state if the lock is held by someone else */
            if (pmutex->suspend == mx_null)
            {
                pmutex->suspend = &activetd->list;
                mx_thread_suspend(activetd, mx_null, 0);
            }
            else
            {
                mx_thread_suspend(activetd, pmutex->suspend, 0);
            }
        }
    }
}

/**
 * try to enter the mutex, the thread will not be blocked when get the lock fall
 * @param pmutex: the descriptor of mutex
 * @return: the result of enter mutex
 */
mx_bool_t mx_mutex_tryenter (mx_mutex_pt pmutex)
{
    mx_bool_t lock;
    mx_thread_pt activetd = mx_conductor_active();

    if (activetd != mx_null)
    {
        lock = mx_atom_flag_lock(&pmutex->flag, activetd);
    }
    else
    {
        lock = mx_true;
    }

    return lock;
}

/**
 * leave the mutex
 * @param pmutex: the descriptor of mutex
 * @return: void
 */
void mx_mutex_leave (mx_mutex_pt pmutex)
{
    mx_thread_pt activetd = mx_conductor_active();

    if (activetd != mx_null)
    {
        mx_atom_flag_unlock(&pmutex->flag, activetd);
        if (pmutex->suspend != mx_null)
        {
            mx_dlist_pt temp = pmutex->suspend;
            pmutex->suspend = mx_null;
            mx_thread_resumeall(temp);
        }
    }
}
